`include "defines.v"
/* verilator lint_off LITENDIAN */
// TODO: Icache 对SRAM有 结构冲突  可以利用交叠的控制 减少大部分冲突 减少阻塞
module PipelineIcache(
  input clk,
  input rst_n,
  input flush,
  input internaFlush,
  input invalidate,
  // request port 
  input                 ifu_icache_addr_valid_i,
  output                icache_ifu_addr_ready_o,
  input  [`VADDR_W-1:0] ifu_icache_vaddr_i     ,
  input                 ifu_icache_pre_i       ,
  // MMU address translation
  output                 icache_mmu_trans_valid_o,
  input                  mmu_icache_trans_ready_i,
  output [`VADDR_W-1:0]  icache_mmu_trans_vaddr_o,
  input  [`PADDR_W-1:0]  mmu_icache_trans_paddr_i,
  // resp port
  output                icache_ibf_data_valid_o,
  input                 ibf_icache_data_ready_i,
  output [`VADDR_W-1:0] icache_ibf_vaddr_o     ,
  output [31:0]         icache_ibf_instr_o     ,
  output                icache_ibf_pre_o       ,
  // mem port
  output                 icache_mem_paddr_valid_o,
  output [`PADDR_W-1:0]  icache_mem_paddr_o      ,
  input                  mem_icache_data_valid_i ,
  input  [127:0]         mem_icache_data_i        
);
/* stage 1 wire */
wire                 icache_s1_addr_valid  ;
wire                 s1_icache_addr_ready  ;
wire [`VADDR_W-1:0]  icache_s1_vaddr       ;
wire                 s1_s2_valid           ;
wire                 s2_s1_ready           ;
wire [`PADDR_W-1:0]  s1_s2_paddr           ;
wire [`VADDR_W-1:0]  s1_s2_vaddr           ;
wire                 s1_icache_trans_valid ;
wire                 icache_s1_trans_valid ;
wire [`VADDR_W-1:0]  s1_icache_trans_vaddr ;
wire [`PADDR_W-1:0]  icache_s1_trans_paddr ;
wire                 s1_sram_valid         ;
wire                 sram_s1_ready         ;
wire [5:0]           s1_sram_index         ;
wire s1_s2_pre;
wire s2_s3_pre;
/* stage 2 wire */
wire                 s2_S3_valid      ;
wire                 s3_s2_ready      ;
wire                 s2_s3_hit        ;
wire [0:3]           s2_s3_replaceWay ;
wire [31:0]          s2_s3_instr      ;
wire [`PADDR_W-1:0]  s2_s3_paddr      ;
wire [`VADDR_W-1:0]  s2_s3_vaddr      ;
wire                 sram_s2_valid    ;
wire [127:0]         sram_s2_dout1    ;
wire [127:0]         sram_s2_dout2    ;
wire [127:0]         sram_s2_dout3    ;
wire [127:0]         sram_s2_dout4    ;
wire [0:3]           s3_s2_meta_wen   ;
wire [5:0]           s3_s2_meta_index ;
wire [20:0]          s3_s2_meta_data  ;
/* stage 3 wire */
wire                 s3_icache_data_valid ;
wire                 icache_s3_data_ready ;
wire [`VADDR_W-1:0]  s3_icache_vaddr      ;
wire [31:0]          s3_icache_instr      ;
wire                 s3_icache_paddr_valid;
wire [`PADDR_W-1:0]  s3_icache_paddr      ;
wire                 icache_s3_data_valid ;
wire [127:0]         icache_s3_data       ;
wire                 s3_sram_valid        ;
wire                 sram_s3_ready        ;
wire [5:0]           s3_sram_index        ;
wire [0:3]           s3_sram_wen          ;
wire [127:0]         s3_sram_din          ;
// 对物理地址 8000_0000 以上的地址缓存 以下地址为外设
// addr 32  -> |外设位 1bit| Tag 21bit | index 6bit | word 2bit | Byte 2bit  
// Meta V 1 TAG 21 

IcacheStage1 s1(
  .clk  (clk  ),
  .rst_n(rst_n),
  .flush(flush),
  // in
  .icache_s1_addr_valid_i ( ifu_icache_addr_valid_i ),
  .s1_icache_addr_ready_o ( icache_ifu_addr_ready_o ),
  .icache_s1_vaddr_i      ( ifu_icache_vaddr_i      ),
  .icache_s1_pre_i        ( ifu_icache_pre_i        ),
  // out
  .s1_s2_valid_o ( s1_s2_valid ),
  .s2_s1_ready_i ( s2_s1_ready ),
  .s1_s2_paddr_o ( s1_s2_paddr ),
  .s1_s2_vaddr_o ( s1_s2_vaddr ),
  .s1_s2_pre_o   ( s1_s2_pre   ),
  // MMU port
  .s1_icache_trans_valid_o (icache_mmu_trans_valid_o ),
  .icache_s1_trans_valid_i (mmu_icache_trans_ready_i ),
  .s1_icache_trans_vaddr_o (icache_mmu_trans_vaddr_o ),
  .icache_s1_trans_paddr_i (mmu_icache_trans_paddr_i ),
  // SRAM port 
  .s1_sram_valid_o ( s1_sram_valid ),
  .sram_s1_ready_i ( sram_s1_ready ),
  .s1_sram_index_o ( s1_sram_index )
);

IcacheStage2 s2(
  .clk  (clk  ),
  .rst_n(rst_n),
  .flush(flush),
  .invalidate(invalidate),
  // s1 in
  .s1_s2_valid_i ( s1_s2_valid ),
  .s2_s1_ready_o ( s2_s1_ready ),
  .s1_s2_paddr_i ( s1_s2_paddr ),
  .s1_s2_vaddr_i ( s1_s2_vaddr ),
  .s1_s2_pre_i   ( s1_s2_pre   ),
  // s2 out
  .s2_S3_valid_o( s2_S3_valid ),
  .s3_s2_ready_i( s3_s2_ready ),
  .s2_s3_hit_o  ( s2_s3_hit   ),
  .s2_s3_instr_o( s2_s3_instr ),
  .s2_s3_paddr_o( s2_s3_paddr ),
  .s2_s3_vaddr_o( s2_s3_vaddr ),
  .s2_s3_replaceWay_o ( s2_s3_replaceWay  ), 
  .s2_s3_pre_o  ( s2_s3_pre   ),
  // sram in
  .sram_s2_valid_i (1'b1),
  .sram_s2_dout1 ( sram_s2_dout1 ),
  .sram_s2_dout2 ( sram_s2_dout2 ),
  .sram_s2_dout3 ( sram_s2_dout3 ),
  .sram_s2_dout4 ( sram_s2_dout4 ),
  // s3 meta write port && bypass
  .s3_s2_meta_wen_i   ( s3_s2_meta_wen   ),
  .s3_s2_meta_index_i ( s3_s2_meta_index ),
  .s3_s2_meta_data_i  ( s3_s2_meta_data  ),
  .s3_sram_data_i     ( s3_sram_din      ) 
);
IcacheStage3 s3(
  .clk  (clk  ),
  .rst_n(rst_n),
  .flush(flush),
  .internaFlush(internaFlush),
  // s2 in   
  .s2_S3_valid_i ( s2_S3_valid ),
  .s3_s2_ready_i ( s3_s2_ready ),
  .s2_s3_hit_i   ( s2_s3_hit   ),
  .s2_s3_instr_i ( s2_s3_instr ),
  .s2_s3_paddr_i ( s2_s3_paddr ),
  .s2_s3_vaddr_i ( s2_s3_vaddr ),
  .s2_s3_replaceWay_i ( s2_s3_replaceWay ), 
  .s2_s3_pre_i   ( s2_s3_pre   ), 
  // resp port
  .s3_icache_data_valid_o ( icache_ibf_data_valid_o ),
  .icache_s3_data_ready_i ( ibf_icache_data_ready_i ),
  .s3_icache_vaddr_o      ( icache_ibf_vaddr_o      ),
  .s3_icache_instr_o      ( icache_ibf_instr_o      ),
  .s3_icache_pre_o        ( icache_ibf_pre_o        ),
  // mem port
  .s3_icache_paddr_valid_o ( icache_mem_paddr_valid_o ),
  .s3_icache_paddr_o       ( icache_mem_paddr_o       ),
  .icache_s3_data_valid_i  ( mem_icache_data_valid_i  ),
  .icache_s3_data_i        ( mem_icache_data_i        ),
  // sram port 
  .s3_sram_valid_o ( s3_sram_valid ),  
  .sram_s3_ready_i ( sram_s3_ready ),
  .s3_sram_index_o ( s3_sram_index ),
  .s3_sram_wen_o   ( s3_sram_wen   ),
  .s3_sram_din_o   ( s3_sram_din   ),
  // meta write port
  .s3_s2_meta_wen_o   ( s3_s2_meta_wen   ),
  .s3_s2_meta_index_o ( s3_s2_meta_index ),
  .s3_s2_meta_data_o  ( s3_s2_meta_data  )
);

Sram4ArrayHasArbiter sram4Array(
  .clk(clk),
  .rst_n(rst_n),
  // 1 write 
  .wr_valid ( s3_sram_valid ),
  .wr_ready ( sram_s3_ready ),
  .wen      ( s3_sram_wen   ),
  .wr_index ( s3_sram_index ),
  .din      ( s3_sram_din   ),
  
  // 4 read
  .rd_valid ( s1_sram_valid ),
  .rd_ready ( sram_s1_ready ), 
  .rd_index ( s1_sram_index ),
  .dout0    ( sram_s2_dout1 ),
  .dout1    ( sram_s2_dout2 ),
  .dout2    ( sram_s2_dout3 ),
  .dout3    ( sram_s2_dout4 )
);

endmodule

module IcacheStage1(
  input clk,
  input rst_n,
  // control 
  input flush,
  // in
  input                  icache_s1_addr_valid_i,
  output                 s1_icache_addr_ready_o,
  input  [`VADDR_W-1:0]  icache_s1_vaddr_i     ,
  input                  icache_s1_pre_i       ,
  // out
  output                 s1_s2_valid_o,
  input                  s2_s1_ready_i,
  output [`PADDR_W-1:0]  s1_s2_paddr_o,
  output [`VADDR_W-1:0]  s1_s2_vaddr_o,
  output                 s1_s2_pre_o  ,
  // MMU port
  output                 s1_icache_trans_valid_o,
  input                  icache_s1_trans_valid_i,
  output [`VADDR_W-1:0]  s1_icache_trans_vaddr_o,
  input  [`PADDR_W-1:0]  icache_s1_trans_paddr_i,
  // SRAM port 
  output                 s1_sram_valid_o,
  input                  sram_s1_ready_i,
  output [5:0]           s1_sram_index_o
);
// function
// 1. address translation vaddr-> paddr
// 2. data sram access  (need paddr[9:4])

reg s1Valid;
reg [`VADDR_W-1:0] vaddr_q;
reg pre_q;
// s1 -> s2 handshake
wire icache_s1_hs = icache_s1_addr_valid_i && s1_icache_addr_ready_o;
wire s1_s2_hs = s1_s2_valid_o && s2_s1_ready_i;
wire s1_sram_hs = s1_sram_valid_o && sram_s1_ready_i;
//前级信号可以进入的条件 1.V为空  2.后一级握手成功 并且处于非刷新时刻
assign s1_icache_addr_ready_o = (~s1Valid || s1_s2_hs) && ~flush;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    s1Valid <= 1'b0;
  else if(flush)
    s1Valid <= 1'b0;
  else if(s1_icache_addr_ready_o)
    s1Valid <= icache_s1_addr_valid_i;

always@(posedge clk)
  if(icache_s1_hs)begin
    vaddr_q <= icache_s1_vaddr_i;
    pre_q   <= icache_s1_pre_i;
  end

// TLB
assign s1_icache_trans_valid_o = s1Valid;
assign s1_icache_trans_vaddr_o = vaddr_q;

// 下一级信号有效条件  寄存器数据有效 且 转换地址有效 且SRAM 请求握手成功
assign s1_s2_valid_o = s1Valid && icache_s1_trans_valid_i && s1_sram_hs;
assign s1_s2_paddr_o = icache_s1_trans_paddr_i;
assign s1_s2_vaddr_o = vaddr_q;
assign s1_s2_pre_o   = icache_s1_pre_i  ;
// SRAM
assign s1_sram_valid_o = s1Valid && icache_s1_trans_valid_i;
assign s1_sram_index_o = icache_s1_trans_paddr_i[9:4];

endmodule

module IcacheStage2(
  input clk,
  input rst_n,
  input flush,
  input invalidate,
  // s1 in
  input                  s1_s2_valid_i,
  output                 s2_s1_ready_o,
  input  [`PADDR_W-1:0]  s1_s2_paddr_i,
  input  [`VADDR_W-1:0]  s1_s2_vaddr_i,
  input                  s1_s2_pre_i  ,
  // s2 out
  output                 s2_S3_valid_o,
  input                  s3_s2_ready_i,
  output                 s2_s3_hit_o,
  output [0:3]           s2_s3_replaceWay_o, //onehot
  output [31:0]          s2_s3_instr_o,
  output [`PADDR_W-1:0]  s2_s3_paddr_o,
  output [`VADDR_W-1:0]  s2_s3_vaddr_o,
  output                 s2_s3_pre_o  ,
  // sram in
  input                  sram_s2_valid_i,
  input  [127:0]         sram_s2_dout1,
  input  [127:0]         sram_s2_dout2,
  input  [127:0]         sram_s2_dout3,
  input  [127:0]         sram_s2_dout4,
  // s3 meta write port && bypass
  input  [0:3]           s3_s2_meta_wen_i,
  input  [5:0]           s3_s2_meta_index_i,
  input  [20:0]          s3_s2_meta_data_i,
  input  [127:0]         s3_sram_data_i
);
// function
// 1.hit check          ok
// 2.bypass check       ok
// 3.sram dout select   ok
// 4.replaceWay select  ok
// 5.clear all metaV    ok
// 6.update meta        ok

reg s2Valid;
reg [`PADDR_W-1:0]  q_paddr;
reg [`VADDR_W-1:0]  q_vaddr;
reg                 q_pre;
wire s1_s2_hs = s2_s1_ready_o && s1_s2_valid_i;
wire s2_s3_hs = s2_S3_valid_o && s3_s2_ready_i;
// valid为空 或者 下一级握手
assign s2_s1_ready_o = ~s2Valid || s2_s3_hs;
// 数据有效 且 sram数据有效
assign s2_S3_valid_o =  s2Valid && sram_s2_valid_i;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    s2Valid <= 1'b0;
  else if (flush)
    s2Valid <= 1'b0;
  else if(s2_s1_ready_o)
    s2Valid <= s1_s2_valid_i;

always@(posedge clk )begin
  if(s1_s2_hs)begin
    q_paddr <= s1_s2_paddr_i;
    q_vaddr <= s1_s2_vaddr_i;
    q_pre   <= s1_s2_pre_i; 
  end
end
assign s2_s3_paddr_o = q_paddr;
assign s2_s3_vaddr_o = q_vaddr;
assign s2_s3_pre_o   = q_pre  ;
reg meta0V [0:63] ;reg [20:0]meta0Tag [0:63];
reg meta1V [0:63] ;reg [20:0]meta1Tag [0:63];
reg meta2V [0:63] ;reg [20:0]meta2Tag [0:63];
reg meta3V [0:63] ;reg [20:0]meta3Tag [0:63];
reg [2:0] plru [0:63]; // B0 B1 B2
/************ hit check ***********************/
wire [5:0] index = {6{s2Valid}} & q_paddr[9:4];
wire [0:3] hit_vector;
assign hit_vector[0]  = q_paddr[31] & meta0V[index] && (meta0Tag[index] == q_paddr[30:10]);
assign hit_vector[1]  = q_paddr[31] & meta1V[index] && (meta1Tag[index] == q_paddr[30:10]);
assign hit_vector[2]  = q_paddr[31] & meta2V[index] && (meta2Tag[index] == q_paddr[30:10]);
assign hit_vector[3]  = q_paddr[31] & meta3V[index] && (meta3Tag[index] == q_paddr[30:10]);
wire debug_meta       = meta3V[index] ;
wire [20:0] debug_tag = meta3Tag[index];
wire   bypass_hit     = q_paddr[31] && (s3_s2_meta_data_i == q_paddr[30:10]) && |s3_s2_meta_wen_i && (index == s3_s2_meta_index_i);
assign s2_s3_hit_o =   |hit_vector || bypass_hit;
// 根据命中的way选择相应的数据
wire [127:0] hit_data = ({128{hit_vector[0]}} & sram_s2_dout1 )| 
                        ({128{hit_vector[1]}} & sram_s2_dout2 )| 
                        ({128{hit_vector[2]}} & sram_s2_dout3 )| 
                        ({128{hit_vector[3]}} & sram_s2_dout4 );
wire [127:0] out_data = bypass_hit ? s3_sram_data_i : hit_data;

wire [31:0] s2_s3_instr  = ({32{(q_paddr[3:2] == 2'd0)}} & out_data[31: 0] )|
                           ({32{(q_paddr[3:2] == 2'd1)}} & out_data[63:32] )|
                           ({32{(q_paddr[3:2] == 2'd2)}} & out_data[95:64] )|
                           ({32{(q_paddr[3:2] == 2'd3)}} & out_data[127:96]);
reg [31:0] instrSave ;
reg s1_s2_hs_r;
reg saveFlag;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    saveFlag <= 1'b0;
  else if(flush)
    saveFlag <= 1'b0;
  else if(s1_s2_hs_r && ~s2_s3_hs && s2Valid)
    saveFlag <= 1'b1;
  else if(s2_s3_hs)
    saveFlag <= 1'b0;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    s1_s2_hs_r <= 1'b0;
  else
    s1_s2_hs_r <= s1_s2_hs;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    instrSave <= 'd0;
  else if(s1_s2_hs_r || bypass_hit)
    instrSave <= s2_s3_instr;

// 如果当拍传不出去 保存信息
assign s2_s3_instr_o = (saveFlag && ~bypass_hit ) ? instrSave : s2_s3_instr;

/************ find repalce way ***********************/
wire [0:3] invalid_victor = ~{meta0V[index],meta1V[index],meta2V[index],meta3V[index]};
wire invalid = |invalid_victor;
// PLRU tree  
//        ALL valid
//       B0         N
//     /     \    invalidWay
//    B1     B2
//   /  \   /  \
//  L0  L1 L2 L3
wire [0:3] plru_way = { ~plru[index][2] & ~plru[index][1] ,
                        ~plru[index][2] &  plru[index][1] ,
                         plru[index][2] & ~plru[index][0] ,
                         plru[index][2] &  plru[index][0] };

wire [0:3] dec_way = invalid ? invalid_victor : plru_way;

reg [0:3]replace_way ;
always@(*)begin
  replace_way = 4'b1000;
  if(dec_way[0]) replace_way = 4'b1000;
  if(dec_way[1]) replace_way = 4'b0100;
  if(dec_way[2]) replace_way = 4'b0010;
  if(dec_way[3]) replace_way = 4'b0001;
end
assign s2_s3_replaceWay_o = replace_way;
/************ update meta ***********************/
function UpdatePlru;
  input [0:3] sel;
  input [0:2] plruVal;
  begin
    UpdatePlru = sel[0] ? {1'b1 , 1'b1 ,  plruVal[0]} :
                 sel[1] ? {1'b1 , 1'b0 ,  plruVal[0]} :
                 sel[2] ? {1'b0 , plruVal[1] ,  1'b1} :
                 sel[3] ? {1'b0 , plruVal[1] ,  1'b0} : plruVal;
  end
endfunction

genvar i ;
generate 
  for(i=0;i<64;i=i+1)begin
    // way 0
    always@(posedge clk or negedge rst_n)
      if(~rst_n)begin
        meta0V[i]   <= 'd0;
        meta0Tag[i] <= 'd0;
      end else if(invalidate)begin
        meta0V[i]   <= 'd0;
      end else if(s3_s2_meta_wen_i[0] && (s3_s2_meta_index_i == i))begin
        meta0V[i]   <= 1'b1;
        meta0Tag[i] <= s3_s2_meta_data_i[20:0];
      end
    // way 1
    always@(posedge clk or negedge rst_n)
      if(~rst_n)begin
        meta1V[i]   <= 'd0;
        meta1Tag[i] <= 'd0;
      end else if(invalidate)begin
        meta1V[i]   <= 'd0;
      end else if(s3_s2_meta_wen_i[1] && (s3_s2_meta_index_i == i))begin
        meta1V[i]   <= 1'b1;
        meta1Tag[i] <= s3_s2_meta_data_i[20:0];
      end
    // way 2
    always@(posedge clk or negedge rst_n)
      if(~rst_n)begin
        meta2V[i]   <= 'd0;
        meta2Tag[i] <= 'd0;
      end else if(invalidate)begin
        meta2V[i]   <= 'd0;
      end else if(s3_s2_meta_wen_i[2] && (s3_s2_meta_index_i == i))begin
        meta2V[i]   <= 1'b1;
        meta2Tag[i] <= s3_s2_meta_data_i[20:0];
      end
    // way 3
    always@(posedge clk or negedge rst_n)
      if(~rst_n)begin
        meta3V[i]   <= 'd0;
        meta3Tag[i] <= 'd0;
      end else if(invalidate)begin
        meta3V[i]   <= 'd0;
      end else if(s3_s2_meta_wen_i[3] && (s3_s2_meta_index_i == i))begin
        meta3V[i]   <= 1'b1;
        meta3Tag[i] <= s3_s2_meta_data_i[20:0];
      end
      always@(posedge clk or negedge rst_n)
        if(~rst_n)
          plru[i] <= 'd0;
        else if(invalidate)begin
          plru[i] <= 'd0;
        end else if((index == i) || (s3_s2_meta_index_i == i))begin
          plru[i] <= UpdatePlru(hit_vector,UpdatePlru(s3_s2_meta_wen_i,plru[i]));
        end
  end
endgenerate
endmodule

module IcacheStage3(
  input clk,
  input rst_n,
  input flush,
  input internaFlush,
  // s2 in   
  input                  s2_S3_valid_i,
  output                 s3_s2_ready_i,
  input                  s2_s3_hit_i,
  input  [0:3]           s2_s3_replaceWay_i, //onehot
  input  [31:0]          s2_s3_instr_i,
  input  [`PADDR_W-1:0]  s2_s3_paddr_i,
  input  [`VADDR_W-1:0]  s2_s3_vaddr_i,
  input                  s2_s3_pre_i  ,
  // resp port
  output                 s3_icache_data_valid_o,
  input                  icache_s3_data_ready_i,
  output [`VADDR_W-1:0]  s3_icache_vaddr_o     ,
  output [31:0]          s3_icache_instr_o     ,
  output                 s3_icache_pre_o       ,
  // mem port
  output                 s3_icache_paddr_valid_o,
  output [`PADDR_W-1:0]  s3_icache_paddr_o      ,
  input                  icache_s3_data_valid_i ,
  input  [127:0]         icache_s3_data_i       ,
  // sram port 
  output                 s3_sram_valid_o,  
  input                  sram_s3_ready_i,
  output [5:0]           s3_sram_index_o,
  output [0:3]           s3_sram_wen_o  ,
  output [127:0]         s3_sram_din_o  ,
  // meta write port
  output [0:3]           s3_s2_meta_wen_o,
  output [5:0]           s3_s2_meta_index_o,
  output [20:0]          s3_s2_meta_data_o
);
// function
// 1.resp to ibf  ok
// 2.mmio access  ok
// 3.mem refill (sram write , meta write)ok
// 4.refill bypass ok
// 5.invalidate   ok
reg [2:0] cur_state;
reg [2:0] nxt_state;
reg s3Valid , flushR;
wire [31:0] bypass_data;
parameter IDLE   = 3'b001,
          READ   = 3'b010,
          REFILL = 3'b100;
wire bypass;
reg bypassR;
wire mmioBypass;
wire s3_icache_hs = s3_icache_data_valid_o && icache_s3_data_ready_i;
wire s2_s3_hs     = s3_s2_ready_i && s2_S3_valid_i;
assign s3_s2_ready_i = ~flushR && (~s3Valid || s3_icache_hs);

reg                 q_hit        ;
reg [0:3]           q_replaceWay ;
reg [31:0]          q_instr      ;
reg [`PADDR_W-1:0]  q_paddr      ;
reg [`VADDR_W-1:0]  q_vaddr      ;
reg                 q_pre        ;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    s3Valid <= 1'b0;
  else if(flush || internaFlush)
    s3Valid <= 1'b0;
  else if(s3_s2_ready_i)
    s3Valid <= s2_S3_valid_i;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    flushR <= 1'b0;
  else if((cur_state == READ) && (nxt_state == IDLE)) // 撤销完成
    flushR <= 1'b0;
  else if((cur_state == REFILL) && (nxt_state == IDLE)) // 撤销完成
    flushR <= 1'b0;
  else if(flush && (cur_state != IDLE) )
    flushR <= 1'b1;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    bypassR <= 1'b0;
  else if(s3_icache_hs)
    bypassR <= 1'b0;
  else if(s3Valid && bypass )
    bypassR <= 1'b1;

assign mmioBypass = ~q_paddr[`PADDR_W-1]; 

always@(posedge clk)begin
    if(s2_s3_hs)begin
      q_hit        <=  s2_s3_hit_i        ;
      q_replaceWay <=  s2_s3_replaceWay_i ;
      q_paddr      <=  s2_s3_paddr_i      ;
      q_vaddr      <=  s2_s3_vaddr_i      ;
      // 旁路阶段 没有握手 载入旁路数据 否则载入上一级信号
      q_instr      <=  s2_s3_instr_i;
      q_pre        <=  s2_s3_pre_i;
    end else if(bypass) begin
      q_instr      <=  bypass_data  ;
    end
end

/* 给下一级的回复信号 */
// 1.数据有效且命中且不为mmio访问 2.refill bypass(包含mmio和refill)
assign s3_icache_data_valid_o = (~mmioBypass && q_hit && s3Valid) || (bypass && ~flushR) || bypassR;
assign s3_icache_vaddr_o      = q_vaddr;
assign s3_icache_instr_o      = bypass ? bypass_data : q_instr ;
assign s3_icache_pre_o        = q_pre;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    cur_state <= IDLE;
  else  
    cur_state <= nxt_state;

always@(*)begin
  case(cur_state)
    IDLE: // 有效状态 + 未命中或者MMIO + MEM访问完成正在等待下一级准备好
      if(s3Valid && (~q_hit || mmioBypass ) && ~bypassR && ~flush)
        nxt_state = READ;
      else
        nxt_state = IDLE;
    READ:
      // 刷新命令并且mem返回数据时 回到初始态 
      if((flush || flushR) && icache_s3_data_valid_i) 
        nxt_state = IDLE;
      // 正常访存时 接收到返回有效信号 进入到重填阶段
      else if(icache_s3_data_valid_i)
        nxt_state = REFILL;
      else
        nxt_state = READ;
    REFILL:
      // mem请求返回时，等待sram写入完成  mmio请求返回时，直接回到空闲
      if(sram_s3_ready_i || mmioBypass)
        nxt_state = IDLE;
      else
        nxt_state = REFILL;
    default:nxt_state=IDLE;
  endcase
end
// mem req
assign s3_icache_paddr_valid_o = (cur_state == READ);
assign s3_icache_paddr_o       = q_paddr;
assign bypass                  = (cur_state == REFILL) && (nxt_state == IDLE); // 重填结束后才旁路数据
// 根据 mmio请求 和 低位地址 选出所需要的指令
// TODO：可以全部返回 与解码配合增大取指速度
assign bypass_data             = mmioBypass ? icache_s3_data_i[31:0] :
                                  ({`PADDR_W{(q_paddr[3:2] == 2'd0)}} & icache_s3_data_i[31: 0] )|
                                  ({`PADDR_W{(q_paddr[3:2] == 2'd1)}} & icache_s3_data_i[63:32] )|
                                  ({`PADDR_W{(q_paddr[3:2] == 2'd2)}} & icache_s3_data_i[95:64] )|
                                  ({`PADDR_W{(q_paddr[3:2] == 2'd3)}} & icache_s3_data_i[127:96]);
// sram data write
assign s3_sram_valid_o = (cur_state == REFILL) && ~mmioBypass;
assign s3_sram_index_o = q_paddr[9:4];
assign s3_sram_wen_o   = q_replaceWay & {4{(cur_state == REFILL) && ~mmioBypass}} ;
assign s3_sram_din_o   = icache_s3_data_i;
// meta write
assign s3_s2_meta_wen_o   = s3_sram_wen_o;
assign s3_s2_meta_index_o = s3_sram_index_o;
assign s3_s2_meta_data_o  = q_paddr[`PADDR_W-2:10];
endmodule

module Sram4ArrayHasArbiter(
  input clk,
  input rst_n,
  // 1 write 
  input  wr_valid,
  output wr_ready,
  input [0:3]   wen,
  input [5:0]   wr_index,
  input [127:0] din,
  
  // 4 read
  input  rd_valid,
  output rd_ready, 
  input  [5:0] rd_index,
  output [127:0] dout0,
  output [127:0] dout1,
  output [127:0] dout2,
  output [127:0] dout3
);

// Arbiter
parameter NONE = 3'b001,
          USR1 = 3'b010,
          USR2 = 3'b100;
reg [2:0] state;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    state <= NONE;
  else begin
    case(state)
      NONE:
        if(wr_valid)
          state <= USR2;
        else if(rd_valid)
          state <= USR1;
      USR1:
        if(wr_valid)
          state <= USR2;
        else if(rd_valid)
          state <= USR1;
        else
          state <= NONE;
      USR2:
        if(rd_valid)
          state <= USR1;
        else if(wr_valid)
          state <= USR2;
        else
          state <= NONE;
      default:state<=NONE;
    endcase
  end

assign wr_ready = (state == USR2);
assign rd_ready = (state == USR1);

wire [5:0] sram_addr = (state == USR2) ? wr_index : rd_index; 

wire [127:0] dout [0:3];
genvar i;
generate  
  for(i=0;i<4;i=i+1)begin : sram_array
    SRAM sram(
      .clk(clk),
      .cs(state != NONE), // WFI 省电
      .wen(wen[i] && (state == USR2)),
      .mask(~128'h0),
      .addr(sram_addr),
      .din(din),
      .dout(dout[i])
    );
  end
endgenerate

assign dout0 = dout[0];
assign dout1 = dout[1];
assign dout2 = dout[2];
assign dout3 = dout[3];

endmodule